/*
 * This file is part of the Sx Framework Library.
 * 
 * Copyright (C) 2013 University of Colorado Denver
 * <min.choi@ucdenver.edu> <shane.transue@ucdenver.edu>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy 
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
 * copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in 
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
 * DEALINGS IN THE SOFTWARE.
 */
#ifndef SX_VIEW_CONTROLLER_H
#define SX_VIEW_CONTROLLER_H

#include <sxViewportController.h>
#include <sxInterface_CameraView.h>

/* 
 * Camera Archetype Declaration
 *
 * The definition of the camera archetype allows the view controller to have
 * access to a camera object but still outsource the renderer implementation.
 * When a view controller is created, a camera renderer must be provided. This
 * renderer will be called when the controllers render function is called.
 * Therefore since any type of camera renderer can be provided, this viewport
 * controller can be used with any UI framework and any graphics library (API).
 */
#include <sxCamera_Archetype.h>

namespace Sx {
namespace Interface {

/* Camera Abstract-Renderer Representation. */
using Graphics::Objects::Rendering::RendererImplementation;

/* Camera Archetype (client version of basic camera). */
using Graphics::Archetypes::Objects::Camera_Archetype;

/* Forward declaration for the mouse controller. */
class MouseMovementController;

/*
 * This viewport controller type provides the ability to manipulate the view
 * provided by the viewport by utilizing a camera. The camera can be modified
 * by the controller but the rendering implementation depends on the type of
 * CameraRenderer provided. This allows the rendering of the camera to be
 * disjoint from the camera implementation.
 */
class ViewController : public ViewportController, public Interface_CameraView {
public:
	ViewController(Viewport* viewport, const std::shared_ptr<RendererImplementation>& cameraRenderer);
	virtual ~ViewController();

	/*
	 * Initialize the camera to provide the view for the controlled viewport.
	 */
	virtual bool initialize();

	void update(float dt);

	virtual bool preRender() const;

	/* Render the camera to provide the view for the controlled viewport. */
	virtual void render() const;

	virtual bool postRender() const;

	/* Utilize the resize event for resizing the camera. */
	virtual void resize(int width, int height);

	void setDisplayMode(DisplayMode mode);

	/*
	 * Set the perspective of the view presented by the viewport controlled by
	 * this controller.
	 */
	virtual void setPerspective(Math::Perspective perspective);

	/*
	 * Set the projection of the view presented by the viewport controlled by
	 * this controller.
	 */
	virtual void setProjection(Math::Projection projection);

	/*
	 * Set the view presented by the viewport controlled by this controller.
	 * This function will change the view of the viewport, however if the
	 * projection of the camera is not orthographic, the result will not be
	 * visible until the projection is changed to orthographic.
	 */
	virtual void setView(Math::View view);

	/*
	 * Sets the cameras position to the specified position.
	 */
	void setCameraPosition(float x, float y, float z);
	void setCameraPosition(const Eigen::Vector3f& position);

	/*
	 * Resets the view presented by the viewport controlled by this controller.
	 */
	void resetView();

	/*
	 * Returns the perspective of the view presented by the controlled viewport.
	 */
	Math::Perspective getPerspective() const;

	/*
	 * Returns the projection of the view presented by the controlled viewport.
	 */
	Math::Projection getProjection() const;

	/*
	 * Returns the view presented by the controlled viewport. This view is only
	 * visible when the controlled viewport has an orthographic projection.
	 */
	Math::View getView() const;	

	/*
	 * Sets the mouse controller that defines how the mouse interacts with
	 * this view controller and its camera. NOTE* This pointer is owned by
	 * this class once it is provided and the previous mouse controller is
	 * freed. Once a mouse controller has been provided it must not be modified
	 * outside of this class.
	 */
	void setMouseController(const std::shared_ptr<MouseMovementController>& mouseController);

	/*
	 * Mouse events determine how the cameras view is modified by different
	 * mouse movements and clicks.
	 */
	virtual void mousePressEvent(int x, int y, MouseButton button, ModifierKey modifierKey);
	virtual void mouseReleaseEvent(int x, int y, MouseButton button, ModifierKey modifierKey);
	virtual void mouseClickEvent(int x, int y, MouseButton button, ModifierKey modifierKey);
	virtual void mouseMoveEvent(int x, int y, MouseButton button, ModifierKey modifierKey);
	virtual void mouseWheelEvent(int delta);
	virtual void mouseEnterEvent();
	virtual void mouseExitEvent();

protected:
	/* 
	 * Camera is stored for internal use. For rendering and update events the
	 * cameraArchetype must be used.
	 * POINTER_OWNERSHIP: YES (both)
	 */
	std::shared_ptr<Camera_Archetype> camera;

	/*
	 * The mouse controller provides an interchangeable component that allows the
	 * mouse behavior of this ViewController to be changed or extended.
	 * POINTER OWNERSHIP: NO
	 */
	std::shared_ptr<MouseMovementController> mouseController;

};

}

}

#endif
